 /*******************************************************************************
  * Copyright (c) 2001, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.views.properties.tabbed.view;

 import java.util.ArrayList ;
 import java.util.HashMap ;
 import java.util.HashSet ;
 import java.util.Iterator ;
 import java.util.List ;
 import java.util.Map ;
 import java.util.Set ;

 import org.eclipse.jface.viewers.IFilter;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.ui.views.properties.tabbed.ISectionDescriptor;
 import org.eclipse.ui.views.properties.tabbed.ITypeMapper;

 /**
  * Provides a section filtering mechanism where the selection is an
  * IStructuredSelection and filtering is based on class.
  *
  * @author Anthony Hunter
  */
 public class TabbedPropertyRegistryClassSectionFilter {

     private ITypeMapper typeMapper = null;

     /**
      * constructor.
      */
     protected TabbedPropertyRegistryClassSectionFilter(ITypeMapper typeMapper) {
         super();
         this.typeMapper = typeMapper;
     }

     /**
      * Verifies if the property section extension represented by sectionElement
      * applies to the given input.
      */
     protected boolean appliesToSelection(ISectionDescriptor descriptor,
             ISelection selection) {

         if (selection instanceof IStructuredSelection
             && selection.isEmpty() == false) {

             if (descriptor.getEnablesFor() != ISectionDescriptor.ENABLES_FOR_ANY
                 && ((IStructuredSelection) selection).size() != descriptor
                     .getEnablesFor()) {
                 /**
                  * enablesFor does not match the size of the selection, do not
                  * display section.
                  */
                 return false;
             }

             IFilter filter = descriptor.getFilter();
             
             if (filter != null) {
                 for (Iterator i = ((IStructuredSelection) selection).iterator(); i
                 .hasNext();) {
                     Object object = i.next();

                     if (filter != null && filter.select(object) == false) {
                         /**
                          * filter fails so section does not apply to the selection,
                          * do not display section.
                          */
                         return false;
                     }
                 }
                 /**
                  * filter passes for all objects in the selection.
                  */
                 return true;
             }

             Set effectiveTypes = new HashSet ();

             for (Iterator i = ((IStructuredSelection) selection).iterator(); i
                 .hasNext();) {

                 Object object = i.next();

                 Class remapType = object.getClass();
                 if (typeMapper != null) {
                     remapType = typeMapper.mapType(object);
                 }

                 if (effectiveTypes.add(remapType)) {

                     // the effective types of the selection
 if (appliesToEffectiveType(descriptor, remapType) == false) {
                         return false;
                     }
                 }
             }
         }

         return true;
     }

     private boolean appliesToEffectiveType(ISectionDescriptor descriptor,
             Class inputClass) {

         ArrayList classTypes = getClassTypes(inputClass);

         List sectionInputTypes = descriptor.getInputTypes();
         for (Iterator j = sectionInputTypes.iterator(); j.hasNext();) {
             String type = (String ) j.next();
             if (classTypes.contains(type)) {
                 // found a match
 return true;
             }
         }

         return false;
     }

     /**
      * Returns the classes and interfaces the given target class
      * extends/implements.
      */
     protected ArrayList getClassTypes(Class target) {
         ArrayList result = new ArrayList ();
         // add classes
 List classes = computeClassOrder(target);
         for (Iterator i = classes.iterator(); i.hasNext();) {
             result.add(((Class ) i.next()).getName());
         }
         // add interfaces
 result.addAll(computeInterfaceOrder(classes));
         return result;
     }

     private List computeClassOrder(Class target) {
         List result = new ArrayList (4);
         Class clazz = target;
         while (clazz != null) {
             result.add(clazz);
             clazz = clazz.getSuperclass();
         }
         return result;
     }

     private List computeInterfaceOrder(List classes) {
         List result = new ArrayList (4);
         Map seen = new HashMap (4);
         for (Iterator iter = classes.iterator(); iter.hasNext();) {
             Class [] interfaces = ((Class ) iter.next()).getInterfaces();
             internalComputeInterfaceOrder(interfaces, result, seen);
         }
         return result;
     }

     private void internalComputeInterfaceOrder(Class [] interfaces, List result,
             Map seen) {
         List newInterfaces = new ArrayList (seen.size());
         for (int i = 0; i < interfaces.length; i++) {
             Class interfac = interfaces[i];
             if (seen.get(interfac) == null) {
                 result.add(interfac.getName());
                 seen.put(interfac, interfac);
                 newInterfaces.add(interfac);
             }
         }
         for (Iterator iter = newInterfaces.iterator(); iter.hasNext();) {
             internalComputeInterfaceOrder(
                 ((Class ) iter.next()).getInterfaces(), result, seen);
         }
     }
 }

